<template>
  <div class="training-plan-container">
    <!-- 主要内容 -->
    <div class="container-fluid p-4">
      <!-- 页面标题和操作按钮 -->
      <el-row justify="space-between" align="middle" class="mb-4">
        <el-col :span="12">
          <h4 class="mb-0">我的培训计划</h4>
        </el-col>
        <el-col :span="12" class="text-end">
          <el-button type="info" plain class="me-2" @click="showFilterModal = true">
            <el-icon><i class="bi bi-funnel"></i></el-icon>
            筛选
          </el-button>
          <el-button type="primary" plain @click="refreshData">
            <el-icon><i class="bi bi-arrow-clockwise"></i></el-icon>
            刷新
          </el-button>
        </el-col>
      </el-row>

      <!-- 培训计划日历视图 -->
      <el-card class="mb-4">
        <template #header>
          <el-tabs v-model="activeTab">
            <el-tab-pane label="日历视图" name="calendar"></el-tab-pane>
            <el-tab-pane label="列表视图" name="list"></el-tab-pane>
            <el-tab-pane label="甘特图" name="gantt"></el-tab-pane>
          </el-tabs>
        </template>
        <div class="p-3">
          <template v-if="activeTab === 'calendar'">
            <el-calendar v-model="calendarValue">
              <template #date-cell="{ data }">
                <div class="calendar-cell" :class="[data.isSelected ? 'is-selected' : '', isToday(data.day) ? 'is-today' : '', !hasEvent(data.day) ? 'no-event' : '']">
                  <div>{{ data.day.split("-").slice(2).join("-") }}</div>
                  <div v-if="hasEvent(data.day)" class="calendar-events">
                    <div
                      v-for="(event, idx) in getEvents(data.day).slice(0,2)"
                      :key="idx"
                      class="calendar-event"
                      :class="event.class"
                      @mouseenter="showEventTooltip($event, event)"
                      @mouseleave="hideEventTooltip"
                      @click="showPlanDetail(event)"
                    >
                      <el-tag :type="event.class === 'bg-success' ? 'success' : 'warning'" size="small" effect="dark">
                        {{ event.title.length > 6 ? event.title.slice(0, 6) + '…' : event.title }}
                      </el-tag>
                    </div>
                    <div v-if="getEvents(data.day).length > 2" class="calendar-event more-events" @click.stop="showAllEvents(data.day)">
                      <el-tag type="info" size="small" effect="plain">+{{ getEvents(data.day).length - 2 }}</el-tag>
                    </div>
                  </div>
                </div>
              </template>
            </el-calendar>
          </template>
          <template v-else-if="activeTab === 'list'">
            <!-- 列表内容 -->
            <div v-if="planStore.loading" class="text-center py-4">
              <el-loading-spinner></el-loading-spinner>
              <p>加载中...</p>
            </div>
            <el-table v-else :data="planStore.filteredPlans" style="width: 100%" stripe border size="small" class="pretty-table">
              <el-table-column prop="plan.title" label="计划名称" align="center" />
              <el-table-column prop="plan.category" label="类型" align="center">
                <template #default="scope">
                  <el-tag :type="scope.row.plan.category === 0 ? 'success' : 'warning'" effect="dark">
                    {{ scope.row.plan.category === 0 ? '课程安排' : '考核节点' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="开始时间" align="center">
                <template #default="scope">{{ formatDate(scope.row.plan.startDate) }}</template>
              </el-table-column>
              <el-table-column label="结束时间" align="center">
                <template #default="scope">{{ formatDate(scope.row.plan.endDate) }}</template>
              </el-table-column>
              <el-table-column prop="status" label="状态" align="center">
                <template #default="scope">
                  <el-tag :type="scope.row.status === 1 ? 'info' : 'primary'" effect="plain">
                    {{ scope.row.status === 1 ? '已完成' : '未完成' }}
                  </el-tag>
                </template>
              </el-table-column>
              <el-table-column label="操作" align="center" width="200">
                <template #default="scope">
                  <el-button 
                    type="info" 
                    size="small" 
                    @click="showPlanDetail(scope.row)"
                    style="margin-right: 8px;"
                  >
                    查看详情
                  </el-button>
                  <el-button 
                    v-if="scope.row.status === 0"
                    type="success" 
                    size="small" 
                    @click="finishPlan(scope.row.plan.id)"
                    :loading="finishingPlanId === scope.row.plan.id"
                  >
                    完成
                  </el-button>
                  <el-tag v-else type="success" effect="plain">已完成</el-tag>
                </template>
              </el-table-column>
            </el-table>
            
            <!-- 分页组件 -->
            <div class="pagination-wrapper mt-4">
              <el-pagination
                v-model:current-page="planStore.pagination.currentPage"
                v-model:page-size="planStore.pagination.pageSize"
                :page-sizes="[10, 20, 50, 100]"
                :total="planStore.pagination.total"
                layout="total, sizes, prev, pager, next, jumper"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
              />
            </div>
          </template>
          <template v-else-if="activeTab === 'gantt'">
            <!-- 甘特图内容 -->
            <div class="gantt-container">
              <div v-for="plan in planStore.filteredPlans" :key="plan.plan.id" class="gantt-row">
                <div class="gantt-title">{{ plan.plan.title }}</div>
                <div class="gantt-bar-wrapper">
                  <div
                    class="gantt-bar"
                    :style="ganttBarStyle(plan.plan.startDate, plan.plan.endDate, plan.plan.category)"
                    @mouseenter="showGanttTooltip($event, plan)"
                    @mouseleave="hideGanttTooltip"
                    @click="showPlanDetail(plan)"
                  >
                    <span class="gantt-bar-label">{{ formatDate(plan.plan.startDate) }} ~ {{ formatDate(plan.plan.endDate) }}</span>
                  </div>
                </div>
                <div class="gantt-status">
                  <el-tag :type="plan.status === 1 ? 'success' : 'warning'" size="small">
                    {{ plan.status === 1 ? '已完成' : '未完成' }}
                  </el-tag>
                </div>
              </div>
            </div>
          </template>
          <el-tooltip v-if="tooltip.visible" :visible="tooltip.visible" :content="tooltip.content" :style="tooltip.style" effect="dark" popper-class="custom-tooltip" />
        </div>
      </el-card>
      
      <el-dialog v-model="showAllEventsDialog" title="当天所有计划" width="350px" :append-to-body="true">
        <div>
          <ul v-if="allEventsOfDay.length" class="all-events-list">
            <li v-for="(event, idx) in allEventsOfDay" :key="idx" style="margin-bottom: 8px; cursor: pointer;">
              <el-tag 
                :type="event.class === 'bg-success' ? 'success' : 'warning'" 
                size="small" 
                effect="dark"
                @click="showPlanDetail(event)"
                style="cursor: pointer;"
              >
                {{ event.title }}
              </el-tag>
            </li>
          </ul>
          <el-empty v-else description="无计划"></el-empty>
        </div>
      </el-dialog>

      <el-row :gutter="20">
        <el-col :span="16">
          <el-card class="mb-4">
            <template #header>
              <div
                class="card-header-el-plus d-flex justify-content-between align-items-center"
              >
                <h5 class="mb-0">即将到来的培训</h5>
                <el-link type="primary" @click="refreshData">查看全部</el-link>
              </div>
            </template>
            <div v-if="planStore.loading" class="text-center py-4">
              <el-loading-spinner></el-loading-spinner>
              <p>加载中...</p>
            </div>
            <el-timeline v-else>
              <el-timeline-item
                v-for="(activity, index) in planStore.upcomingTrainings"
                :key="index"
                placement="top"
              >
                <el-card style="cursor: pointer;" @click="showPlanDetail(activity)">
                  <div class="d-flex justify-content-between align-items-start">
                    <div class="flex-grow-1">
                      <h4>{{ activity.title }}</h4>
                      <p class="text-muted mb-2">
                        {{ activity.type }}<br>
                        <span>开始：{{ formatDate(activity.startDate) }}</span><br>
                        <span>结束：{{ formatDate(activity.endDate) }}</span>
                      </p>
                      <div class="d-flex align-items-center gap-2">
                        <el-tag :type="activity.category === 0 ? 'success' : 'warning'">
                          {{ activity.category === 0 ? '课程安排' : '考核节点' }}
                        </el-tag>
                        <el-tag type="primary">{{ activity.remaining }}</el-tag>
                      </div>
                    </div>
                    <div v-if="activity.status === 0" class="ms-3">
                      <el-button 
                        type="success" 
                        size="small" 
                        @click.stop="finishPlan(activity.planId)"
                        :loading="finishingPlanId === activity.planId"
                      >
                        完成
                      </el-button>
                    </div>
                  </div>
                </el-card>
              </el-timeline-item>
              <el-empty v-if="planStore.upcomingTrainings.length === 0" description="暂无即将到来的培训"></el-empty>
            </el-timeline>
          </el-card>
        </el-col>

        <el-col :span="8">
          <el-card class="mb-4">
            <template #header>
              <div
                class="card-header-el-plus d-flex justify-content-between align-items-center"
              >
                <h5 class="mb-0">学习进度</h5>
                <el-link type="primary" @click="refreshData">查看详情</el-link>
              </div>
            </template>
            <div class="text-center">
              <el-progress
                type="circle"
                :percentage="planStore.learningProgress"
                :width="120"
                class="mb-3"
              ></el-progress>
              <p class="text-muted">已完成 {{ planStore.learningProgress }}% 的培训计划</p>
              <el-button type="primary" size="small" @click="refreshData">继续学习</el-button>
            </div>
          </el-card>
        </el-col>
      </el-row>
    </div>

    <!-- 筛选模态框 -->
    <el-dialog v-model="showFilterModal" title="筛选条件" width="500px">
      <el-form :model="filterForm" label-width="100px">
        <el-form-item label="计划类型">
          <el-select v-model="filterForm.category" placeholder="请选择计划类型" clearable>
            <el-option label="课程安排" :value="0"></el-option>
            <el-option label="考核节点" :value="1"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="时间范围">
          <el-date-picker
            v-model="filterForm.dateRange"
            type="daterange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            format="YYYY-MM-DD"
            value-format="YYYY-MM-DD"
          />
        </el-form-item>
        <el-form-item label="完成状态">
          <el-select v-model="filterForm.status" placeholder="请选择状态" clearable>
            <el-option label="未完成" :value="0"></el-option>
            <el-option label="已完成" :value="1"></el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="clearFilter">清除筛选</el-button>
          <el-button type="primary" @click="applyFilter">确定</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, onMounted, computed } from "vue";
import { usePlanStores } from "@/stores/training-plan.js";
import { useUserStore } from "@/stores/user.js";
import { ElMessage, ElMessageBox } from "element-plus";
import { reactive } from 'vue';

const planStore = usePlanStores();
const userStore = useUserStore();
const calendarValue = ref(new Date());
const activeTab = ref("calendar");
const showFilterModal = ref(false);
const finishingPlanId = ref(null);
const showAllEventsDialog = ref(false);
const allEventsOfDay = ref([]);

// 筛选表单
const filterForm = ref({
  category: null,
  dateRange: [],
  status: null
});

// 日历事件相关方法
const hasEvent = (date) => {
  return planStore.calendarEvents[date] !== undefined;
};

const getEventTitle = (date) => {
  return planStore.calendarEvents[date]?.title;
};

const getEventClass = (date) => {
  return planStore.calendarEvents[date]?.class;
};



// 完成计划
const finishPlan = async (planId) => {
  try {
    finishingPlanId.value = planId;
    await ElMessageBox.confirm(
      '确定要标记这个计划为已完成吗？',
      '确认操作',
      {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }
    );
    
    await planStore.finishPlan(planId);
  } catch (error) {
    if (error !== 'cancel') {
      console.error('完成计划失败:', error);
    }
  } finally {
    finishingPlanId.value = null;
  }
};

// 刷新数据
const refreshData = async () => {
  try {
    await planStore.getStudentPlansPage(
      planStore.pagination.currentPage,
      planStore.pagination.pageSize
    );
  } catch (error) {
    console.error('刷新数据失败:', error);
  }
};

// 应用筛选
const applyFilter = async () => {
  try {
    // 更新store中的筛选条件
    planStore.setFilter(filterForm.value);
    showFilterModal.value = false;
    
    // 重新获取数据
    await planStore.getStudentPlansPage(1, planStore.pagination.pageSize);
    ElMessage.success('筛选条件已应用');
  } catch (error) {
    console.error('应用筛选失败:', error);
    ElMessage.error('应用筛选失败');
  }
};

// 清除筛选
const clearFilter = () => {
  filterForm.value = {
    category: null,
    dateRange: [],
    status: null
  };
  planStore.clearFilter();
  showFilterModal.value = false;
  refreshData();
};

// 分页处理
const handleCurrentChange = async (page) => {
  try {
    await planStore.getStudentPlansPage(page, planStore.pagination.pageSize);
  } catch (error) {
    console.error('分页获取数据失败:', error);
  }
};

const handleSizeChange = async (size) => {
  try {
    await planStore.getStudentPlansPage(1, size);
  } catch (error) {
    console.error('分页获取数据失败:', error);
  }
};

// 格式化日期时间
const formatDate = (date) => {
  if (!date) return '';
  if (typeof date === 'string') date = new Date(date);
  return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit' });
};

// 计算持续时间
const calculateDuration = (startDate, endDate) => {
  if (!startDate || !endDate) return '';
  const start = new Date(startDate);
  const end = new Date(endDate);
  const diffTime = Math.abs(end - start);
  const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
  
  if (diffDays === 1) {
    return '1天';
  } else if (diffDays < 30) {
    return `${diffDays}天`;
  } else if (diffDays < 365) {
    const months = Math.floor(diffDays / 30);
    return `${months}个月`;
  } else {
    const years = Math.floor(diffDays / 365);
    return `${years}年`;
  }
};

// 组件挂载时获取数据
onMounted(async () => {
  try {
    await planStore.getStudentPlansPage(1, 10);
  } catch (error) {
    console.error('初始化数据失败:', error);
  }
});

// 日历多事件支持
const getEvents = (date) => {
  return planStore.calendarEvents[date] || [];
};

// 判断是否为今天
const isToday = (date) => {
  const today = new Date();
  const d = new Date(date);
  return d.getFullYear() === today.getFullYear() && d.getMonth() === today.getMonth() && d.getDate() === today.getDate();
};

// 日历事件tooltip
const tooltip = reactive({ visible: false, content: '', style: {} });
const showEventTooltip = (e, event) => {
  tooltip.visible = true;
  tooltip.content = `计划：${event.title}`;
  tooltip.style = { left: e.clientX + 10 + 'px', top: e.clientY - 10 + 'px', position: 'fixed' };
};
const hideEventTooltip = () => { tooltip.visible = false; };

// 显示所有事件弹窗
const showAllEvents = (date) => {
  allEventsOfDay.value = getEvents(date);
  showAllEventsDialog.value = true;
};

// 甘特图条形宽度计算（美化版，支持不同类型颜色）
const ganttBarStyle = (start, end, category) => {
  const startDate = new Date(start);
  const endDate = new Date(end);
  const min = Math.min(...planStore.filteredPlans.map(p => new Date(p.plan.startDate).getTime()));
  const max = Math.max(...planStore.filteredPlans.map(p => new Date(p.plan.endDate).getTime()));
  const total = Math.max(1, (max - min) / (1000 * 60 * 60 * 24));
  const left = ((startDate - min) / (1000 * 60 * 60 * 24)) / total * 100;
  const width = ((endDate - startDate) / (1000 * 60 * 60 * 24) + 1) / total * 100;
  const color = category === 0
    ? 'linear-gradient(90deg, #67c23a 0%, #b7e4c7 100%)'
    : 'linear-gradient(90deg, #e6a23c 0%, #ffe0b2 100%)';
  return {
    left: left + '%',
    width: width + '%',
    background: color,
    color: '#333',
    borderRadius: '6px',
    padding: '2px 8px',
    position: 'absolute',
    fontSize: '13px',
    minWidth: '80px',
    textAlign: 'center',
    boxShadow: '0 2px 8px rgba(0,0,0,0.08)',
    border: '1px solid #e0e0e0',
    cursor: 'pointer',
    transition: 'box-shadow 0.2s',
  };
};

// 甘特图tooltip
const showGanttTooltip = (e, plan) => {
  tooltip.visible = true;
  tooltip.content = `计划：${plan.plan.title}<br>类型：${plan.plan.category === 0 ? '课程安排' : '考核节点'}<br>时间：${formatDate(plan.plan.startDate)} ~ ${formatDate(plan.plan.endDate)}`;
  tooltip.style = { left: e.clientX + 10 + 'px', top: e.clientY - 10 + 'px', position: 'fixed' };
};
const hideGanttTooltip = () => { tooltip.visible = false; };

// 甘特图点击事件
const handleGanttClick = (plan) => {
  ElMessage.info(`计划：${plan.plan.title} (${plan.status === 1 ? '已完成' : '未完成'})`);
};

// 显示计划详情模态框
const showPlanDetail = (plan) => {
  // 处理不同的数据结构（plan.plan 或直接 plan）
  const planData = plan.plan || plan;
  const status = plan.status !== undefined ? plan.status : (planData.status || 0);
  
  ElMessageBox.alert(`
    <div style="padding: 20px; max-width: 500px;">
      <div style="text-align: center; margin-bottom: 20px;">
        <h3 style="color: #409eff; margin: 0;">${planData.title}</h3>
      </div>
      
      <div style="background: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 15px;">
        <div style="display: flex; align-items: center; margin-bottom: 10px;">
          <span style="font-weight: bold; color: #606266; min-width: 80px;">类型:</span>
          <el-tag style="margin-left: 10px;" :type="planData.category === 0 ? 'success' : 'warning'" effect="dark">
            ${planData.category === 0 ? '课程安排' : '考核节点'}
          </el-tag>
        </div>
        
        <div style="display: flex; align-items: center; margin-bottom: 10px;">
          <span style="font-weight: bold; color: #606266; min-width: 80px;">状态:</span>
          <el-tag style="margin-left: 10px;" :type="status === 1 ? 'success' : 'warning'" effect="plain">
            ${status === 1 ? '已完成' : '未完成'}
          </el-tag>
        </div>
      </div>
      
      <div style="margin-bottom: 15px;">
        <div style="margin-bottom: 8px;">
          <span style="font-weight: bold; color: #606266;">开始时间:</span>
          <span style="margin-left: 10px; color: #303133;">${formatDate(planData.startDate)}</span>
        </div>
        <div style="margin-bottom: 8px;">
          <span style="font-weight: bold; color: #606266;">结束时间:</span>
          <span style="margin-left: 10px; color: #303133;">${formatDate(planData.endDate)}</span>
        </div>
        <div style="margin-bottom: 8px;">
          <span style="font-weight: bold; color: #606266;">持续时间:</span>
          <span style="margin-left: 10px; color: #303133;">${calculateDuration(planData.startDate, planData.endDate)}</span>
        </div>
      </div>
      
      <div style="margin-bottom: 15px;">
        <span style="font-weight: bold; color: #606266;">描述:</span>
        <div style="margin-top: 8px; padding: 10px; background: #f8f9fa; border-radius: 4px; color: #606266; line-height: 1.5;">
          ${planData.description || '暂无描述'}
        </div>
      </div>
      
      ${planData.teacherName ? `
      <div style="margin-bottom: 15px;">
        <span style="font-weight: bold; color: #606266;">负责教师:</span>
        <span style="margin-left: 10px; color: #303133;">${planData.teacherName}</span>
      </div>
      ` : ''}
      
      ${planData.remaining ? `
      <div style="background: #e1f3d8; padding: 10px; border-radius: 4px; border-left: 4px solid #67c23a;">
        <span style="color: #67c23a; font-weight: bold;">⏰ ${planData.remaining}</span>
      </div>
      ` : ''}
    </div>
  `, '计划详情', {
    dangerouslyUseHTMLString: true,
    confirmButtonText: '关闭',
    customClass: 'plan-detail-dialog',
    closeOnClickModal: false,
    center: true,
  });
};
</script>

<style scoped>
.training-plan-container {
  background-color: #f0f2f5;
  min-height: calc(100vh - 60px);
  padding: 20px;
}

.card-header-el-plus {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
}

.el-card__header {
  padding: 0 20px !important;
}

.el-tabs__header {
  margin: 0;
}

.el-tabs__nav-wrap::after {
  height: 0;
}

.el-calendar__body {
  padding: 12px;
}

.calendar-event {
  margin-top: 5px;
  padding: 2px 5px;
  border-radius: 4px;
  color: white;
  font-size: 0.75em;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  cursor: pointer;
  transition: opacity 0.2s;
}

.calendar-event:hover {
  opacity: 0.8;
}

.bg-primary {
  background-color: #007bff !important;
}

.bg-success {
  background-color: #28a745 !important;
}

.bg-info {
  background-color: #17a2b8 !important;
}

.bg-warning {
  background-color: #ffc107 !important;
  color: #212529 !important;
}

.bg-danger {
  background-color: #dc3545 !important;
}

.el-timeline-item__content {
  padding-bottom: 20px;
}

.el-timeline-item__timestamp {
  color: #909399;
  font-size: 13px;
}

.el-progress-circle__track {
  stroke: #e6e6e6;
}

.el-progress-circle__path {
  stroke: #409eff;
}

/* Bootstrap Icons compatibility */
.el-icon {
  vertical-align: middle;
  margin-right: 5px;
}

.el-button .bi {
  line-height: 1;
}

/* 加载状态样式 */
.el-loading-spinner {
  margin: 0 auto;
}

/* 计划详情模态框样式 */
:deep(.plan-detail-dialog) {
  .el-message-box__content {
    padding: 0;
  }
  
  .el-message-box__message {
    padding: 0;
  }
}

/* 甘特图样式优化 */
.gantt-container {
  padding: 20px;
}

.gantt-row {
  display: flex;
  align-items: center;
  margin-bottom: 15px;
  padding: 10px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.gantt-title {
  width: 200px;
  font-weight: bold;
  color: #303133;
  margin-right: 20px;
}

.gantt-bar-wrapper {
  flex: 1;
  position: relative;
  height: 30px;
  background: #f5f7fa;
  border-radius: 4px;
  margin-right: 20px;
}

.gantt-bar {
  position: absolute;
  height: 100%;
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: all 0.3s ease;
}

.gantt-bar:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

.gantt-bar-label {
  font-size: 12px;
  font-weight: 500;
  color: #333;
  white-space: nowrap;
}

.gantt-status {
  width: 100px;
  text-align: center;
}

/* 即将到来的培训卡片样式 */
.el-timeline-item .el-card {
  transition: all 0.3s ease;
}

.el-timeline-item .el-card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

/* 空状态样式 */
.el-empty {
  padding: 40px 0;
}

/* 分页样式 */
.pagination-wrapper {
  display: flex;
  justify-content: center;
  margin-top: 20px;
}

/* 响应式调整 */
@media (max-width: 768px) {
  .training-plan-container {
    padding: 10px;
  }
  
  .el-col {
    margin-bottom: 20px;
  }
}

:deep(.el-calendar-table) {
  border: none !important;
  border-collapse: separate !important;
  border-spacing: 0 !important;
}
:deep(.el-calendar-table td) {
  border: none !important;
  box-shadow: none !important;
  outline: none !important;
  background: transparent !important;
  padding: 0 !important;
  vertical-align: top !important;
}

.calendar-cell {
  min-height: 70px;
  padding: 4px 2px 2px 2px;
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  transition: background 0.2s;
  box-shadow: none !important;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  background: transparent;
}
.calendar-cell.is-today {
  border: 2px solid #409eff;
  background: #eaf6ff;
}
.calendar-cell.no-event{
  color: #bbb;
}
.calendar-day {
  font-size: 15px;
  font-weight: 500;
  margin-bottom: 4px;
  display: block;
  color: #222;
}
.calendar-events {
  display: flex;
  flex-direction: column;
  gap: 2px;
  width: 100%;
  box-sizing: border-box;
}
.calendar-event {
  max-width: 100%;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  margin-bottom: 0;
  display: block;
  border-radius: 6px;
  box-shadow: 0 2px 8px rgba(64,158,255,0.08);
  cursor: pointer;
  transition: box-shadow 0.2s, background 0.2s;
  box-sizing: border-box;
}
.calendar-event.more-events {
  text-align: left;
  cursor: pointer;
  margin-top: 2px;
}
.calendar-event.more-events .el-tag {
  background: #f4f4f5;
  color: #409eff;
  border: 1px dashed #b3d8fd;
}
.calendar-event:hover {
  box-shadow: 0 4px 16px rgba(64,158,255,0.18);
  opacity: 0.95;
  z-index: 2;
}
.el-tag {
  font-size: 12px;
  padding: 0 8px;
  border-radius: 8px;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.is-selected {
  background: #f5faff;
}
.pretty-table .el-table__header th {
  font-weight: bold;
  background: #f8fafc;
  color: #333;
}
.pretty-table .el-table__row {
  transition: background 0.2s;
}
.pretty-table .el-table__row:hover {
  background: #f0f7ff;
}
/* 甘特图样式 */
.gantt-container {
  margin-top: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
  padding: 18px;
  background: #f8fafc;
}
.gantt-row {
  display: flex;
  align-items: center;
  margin-bottom: 22px;
  position: relative;
  height: 38px;
}
.gantt-title {
  width: 140px;
  font-weight: 600;
  font-size: 15px;
  color: #222;
  text-align: right;
  margin-right: 18px;
}
.gantt-bar-wrapper {
  flex: 1;
  position: relative;
  height: 38px;
}
.gantt-bar {
  position: absolute;
  top: 9px;
  height: 22px;
  line-height: 22px;
  border-radius: 6px;
  font-size: 13px;
  min-width: 80px;
  text-align: center;
  box-shadow: 0 2px 8px rgba(64,158,255,0.08);
  border: 1px solid #e0e0e0;
  cursor: pointer;
  transition: box-shadow 0.2s;
  display: flex;
  align-items: center;
  justify-content: center;
}
.gantt-bar-label {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
}
.gantt-bar:hover {
  box-shadow: 0 6px 18px rgba(64,158,255,0.18);
  z-index: 2;
}
.gantt-status {
  width: 80px;
  text-align: center;
  margin-left: 18px;
}
.custom-tooltip {
  font-size: 13px !important;
  line-height: 1.6;
  padding: 8px 12px;
  border-radius: 8px;
  background: #222 !important;
  color: #fff !important;
  max-width: 260px;
  white-space: pre-line;
}
.all-events-list {
  list-style: none;
  padding: 0;
  margin: 0;
}
.all-events-list li {
  margin-bottom: 8px;
}

.me-2 {
  margin-right: 0.5rem;
}

.mt-4 {
  margin-top: 1.5rem;
}

.mb-4 {
  margin-bottom: 1.5rem;
}

.ms-3 {
  margin-left: 1rem;
}

.gap-2 {
  gap: 0.5rem;
}

.text-muted {
  color: #909399;
}

.d-flex {
  display: flex;
}

.justify-content-between {
  justify-content: space-between;
}

.align-items-center {
  align-items: center;
}

.align-items-start {
  align-items: flex-start;
}

.flex-grow-1 {
  flex-grow: 1;
}

.text-center {
  text-align: center;
}

.text-end {
  text-align: right;
}
</style>

